package sms

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/bobo-rs/idea-space-framework/consts"
	"gitee.com/bobo-rs/idea-space-framework/pkg/sredis"
	"gitee.com/bobo-rs/idea-space-framework/pkg/utils"
	"time"
)

const (
	smsCaptchaExpire = time.Minute * 5 // 缓存五分钟
)

// SetCaptchaCode 缓存设置验证码
func (s *sSms) SetCaptchaCode(ctx context.Context, code, mobile string) error {
	// 缓存验证码
	if err := sredis.NewCache().Set(ctx, s.GetCaptchaMobileKey(mobile), code, smsCaptchaExpire); err != nil {
		return fmt.Errorf(`验证码记录失败, %s`, err.Error())
	}
	return nil
}

// GetCaptchaCode 获取缓存验证码
func (s *sSms) GetCaptchaCode(ctx context.Context, mobile string) (*string, error) {
	// 实例缓存客户端
	cacheService := sredis.NewCache()
	if r, err := cacheService.Contains(ctx, s.GetCaptchaMobileKey(mobile)); err != nil || !r {
		return nil, errors.New(`验证码已失效`)
	}

	// 获取验证码
	r, err := cacheService.Get(ctx, s.GetCaptchaMobileKey(mobile))
	if err != nil {
		return nil, fmt.Errorf(`验证码不存在，%s`, err.Error())
	}

	// 读取验证码
	code := r.String()
	if len(code) == 0 {
		return nil, errors.New(`验证码已过期`)
	}
	return &code, nil
}

// RemoveCaptchaCode 移除销毁验证码
func (s *sSms) RemoveCaptchaCode(ctx context.Context, mobile string) {
	_, _ = sredis.NewCache().Remove(ctx, s.GetCaptchaMobileKey(mobile))
}

// GetCaptcha1MinuteMobileNum 获取同一个手机号1分钟发送量
func (s *sSms) GetCaptcha1MinuteMobileNum(ctx context.Context, mobile string) (int, error) {
	// 读取缓存
	r, err := sredis.New().Get(ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha1MinuteLockMobile, mobile))
	if err != nil {
		return 0, fmt.Errorf(`获取手机号缓存发送数量失败%s`, err.Error())
	}
	return r.Int(), nil
}

// SetCaptcha1MinuteMobileNum 同一个手机号1分钟发送量
func (s *sSms) SetCaptcha1MinuteMobileNum(ctx context.Context, mobile string) error {
	// 设置数量
	return s.SetCaptchaSendIncr(
		ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha1MinuteLockMobile, mobile), time.Minute,
	)
}

// SetCaptcha1MinuteMacSendNum 设置同一个MAC地址短信发送量
func (s *sSms) SetCaptcha1MinuteMacSendNum(ctx context.Context) error {
	// 获取KEY
	key, err := s.GetCaptcha1MinuteLockMacKey(ctx)
	if err != nil {
		return err
	}

	// 缓存一分钟
	return s.SetCaptchaSendIncr(ctx, *key, time.Minute)
}

// GetCaptcha1MinuteMacNum 获取同一个MAC地址验证码发送量
func (s *sSms) GetCaptcha1MinuteMacNum(ctx context.Context) (int, error) {
	// 获取KEY
	key, err := s.GetCaptcha1MinuteLockMacKey(ctx)
	if err != nil {
		return 0, err
	}

	// 读取发送数量
	r, err := sredis.New().Get(ctx, *key)
	if err != nil {
		return 0, fmt.Errorf(`获取同一个MAC地址发送量报错%s`, err.Error())
	}
	return r.Int(), nil
}

// GetCaptchaHalfHourMacNum 获取同一个MAC地址半小时验证码发送量
func (s *sSms) GetCaptchaHalfHourMacNum(ctx context.Context) (int, error) {
	// 获取KEY
	key, err := s.GetCaptchaHalfHourLockMacKey(ctx)
	if err != nil {
		return 0, err
	}

	// 读取缓存
	r, err := sredis.New().Get(ctx, *key)
	if err != nil {
		return 0, err
	}
	return r.Int(), nil
}

// SetCaptchaHalfHourMacNum 设置同一个MAC地址半小时发送量
func (s *sSms) SetCaptchaHalfHourMacNum(ctx context.Context) error {
	// 获取KEY
	key, err := s.GetCaptchaHalfHourLockMacKey(ctx)
	if err != nil {
		return err
	}
	// 缓存发送量，缓存半小时
	return s.SetCaptchaSendIncr(ctx, *key, time.Minute*30)
}

// GetCaptcha1HourMobileNum 获取同一个手机号1小时内发送量
func (s *sSms) GetCaptcha1HourMobileNum(ctx context.Context, mobile string) (int, error) {
	// 获取缓存量
	r, err := sredis.New().Get(ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha1HourNumMobile, mobile))
	if err != nil {
		return 0, fmt.Errorf(`读取同一手机号1小时发送量失败%s`, err.Error())
	}
	return r.Int(), nil
}

// SetCaptcha1HourMobileNum 设置同一个手机号1小时内发送量
func (s *sSms) SetCaptcha1HourMobileNum(ctx context.Context, mobile string) error {
	// 设置缓存量1小时
	return s.SetCaptchaSendIncr(
		ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha1HourNumMobile, mobile), time.Minute,
	)
}

// GetCaptcha1MinuteNum 获取验证码1分钟发送量
func (s *sSms) GetCaptcha1MinuteNum(ctx context.Context) (int, error) {
	// 获取发送量
	r, err := sredis.New().Get(ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha1MinuterNum))
	if err != nil {
		return 0, fmt.Errorf(`读取验证码1分钟发送量%s`, err.Error())
	}
	return r.Int(), nil
}

// SetCaptcha1MinuteNum 设置验证码1分钟发送量
func (s *sSms) SetCaptcha1MinuteNum(ctx context.Context) error {
	// 设置发送量缓存
	return s.SetCaptchaSendIncr(
		ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha1MinuterNum), time.Minute,
	)
}

// GetCaptcha1HourNum 获取验证码1小时发送量
func (s *sSms) GetCaptcha1HourNum(ctx context.Context) (int, error) {
	// 获取验证码1小时发送量
	r, err := sredis.New().Get(ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha1HourNum))
	if err != nil {
		return 0, fmt.Errorf(`读取验证码1小时发送量%s`, err.Error())
	}
	return r.Int(), nil
}

// SetCaptcha1HourNum 设置验证码1小时发送数量
func (s *sSms) SetCaptcha1HourNum(ctx context.Context) error {
	// 设置发送量，缓存一小时
	return s.SetCaptchaSendIncr(
		ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha1HourNum), time.Hour,
	)
}

// GetCaptcha24HourNum 获取验证码24小时发送量
func (s *sSms) GetCaptcha24HourNum(ctx context.Context) (int, error) {
	// 读取发送量
	r, err := sredis.New().Get(ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha24HourNum))
	if err != nil {
		return 0, fmt.Errorf(`读取验证码24小时发送量%s`, err.Error())
	}
	return r.Int(), nil
}

// SetCaptcha24HourNum 设置验证码24小时内发送量-避免被恶意刷验证码接口
func (s *sSms) SetCaptcha24HourNum(ctx context.Context) error {
	// 记录发送量 缓存到第二天凌晨零点
	return s.SetCaptchaSendIncr(
		ctx, utils.SmsCaptchaCacheKey(consts.SafeSmsCaptcha24HourNum), utils.ToDayZeroDiff(),
	)
}

// GetCaptchaMobileKey 获取短信验证码发送缓存验证码KEY
func (s *sSms) GetCaptchaMobileKey(mobile string) string {
	return utils.SmsCaptchaCacheKey(consts.SafeSmsCaptchaSendMobile, mobile)
}

// GetCaptcha1MinuteLockMacKey 获取同一个MAC地址一分钟内短信发送量并锁定KEY
func (s *sSms) GetCaptcha1MinuteLockMacKey(ctx context.Context) (*string, error) {
	key := utils.SmsCaptchaCacheKey(consts.SafeSmaCaptcha1MinuteLockIp, utils.GetIp(ctx))
	return &key, nil
}

// GetCaptchaHalfHourLockMacKey 获取同一个MAC地址半小时内短信发送量并锁定KEY
func (s *sSms) GetCaptchaHalfHourLockMacKey(ctx context.Context) (*string, error) {
	key := utils.SmsCaptchaCacheKey(consts.SafeSmsCaptchaHalfHourNumIp, utils.GetIp(ctx))
	return &key, nil
}

// SetCaptchaSendIncr 设置验证码发送原子发送量
func (s *sSms) SetCaptchaSendIncr(ctx context.Context, key string, expire time.Duration) error {
	// 使用Redis的INCR命令原子地增加发送量
	client := sredis.New()
	_, err := client.Incr(ctx, key)
	if err != nil {
		return fmt.Errorf(`%s发送量增加失败%s`, key, err.Error())
	}

	// 设置缓存时间，转换为秒戳
	if _, err = client.Expire(ctx, key, int64(expire.Seconds())); err != nil {
		return fmt.Errorf(`%s发送量增加设置过期时间失败%s`, key, err.Error())
	}
	return nil
}
